# -*- coding: utf-8 -*-
import json
import re
from collections import OrderedDict
from hashlib import sha1

import requests
import xmltodict
from django.conf import settings

from async import async_job
from common.cache import redis_cache
from common.channel.base_pay import BasePay, SignMd5
from common.order.db import get_pay, add_pay_success
from common.utils import track_logging
from common.utils.exceptions import SignError

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {

    'GD00001': {
        'API_KEY': 'ksn76Bg$5f%$js19',
    },
    'gateway': 'https://www.paymentonline515.com/payment.php',
    'query_gateway': 'https://www.paymentonline515.com/querytransaction.php',
}


class XPay(BasePay, SignMd5):
    # 渠道自定义的加密解密方式
    str_split = ["", "g", "h", "G", "k", "g", "J", "K", "I", "h", "i", "j", "H"]
    sp = "" + "g|h|G|k|g|J|K|I|h|i|j|H"
    n = 0
    str_temp = ""

    # 加密
    def encrypt_data(self, to_encrypt):
        if to_encrypt != '' and to_encrypt is not None:
            temp = to_encrypt.replace('\r\n', '')
            for i in range(0, len(temp)):
                number = ord(to_encrypt[i])
                self.str_temp += self.encrypt_to_int16(number)
        return self.str_temp

    # 把ascii吗转为十六进制
    def encrypt_to_int16(self, number):
        if self.n >= 12:
            self.n = 1
        else:
            self.n += 2
        my_char = 'H'
        try:
            my_char = self.str_split[self.n]
        except:
            self.n = 1
        return hex(number).replace('0x', '') + my_char

    # 解密
    def decrypt_data(self, to_decrypt):
        if to_decrypt != '' and to_decrypt is not None:
            st = re.split(self.sp, to_decrypt.replace('\r\n', ''))
            for i in range(0, len(st) - 1):
                self.str_temp += self.decrypt_to_string(st[i])
        return self.str_temp

    # 十六进制转为ascii码，然后ascii码转为字符
    @staticmethod
    def decrypt_to_string(number):
        return chr(int(number, 16))

    # 异步回调签名验证
    def verify_notify_sign(self, parameter, key):
        pending_str = '%s:%s,%s,%s,%s,%s,%s' % (
            key, parameter['RefID'], parameter['Curr'], parameter['Amount'],
            parameter['Status'], parameter['TransID'],
            parameter['ValidationKey']
        )
        sign = parameter['EncryptText']
        calculated_sign = self.md5_sign_upper(pending_str)
        _LOGGER.info('sign:%s,scalculated_sign:%s' % (sign, calculated_sign))
        if sign != calculated_sign:
            _LOGGER.info("xpay sign: %s, calculated sign: %s", sign, calculated_sign)
            raise SignError('sign not pass, data: %s' % parameter)

    @staticmethod
    def str_to_dict(st):
        parm = {}
        for a in st.split('&'):
            s = a.split('=')
            k = s[0]
            v = s[1]
            parm[k] = v
        return parm

    @staticmethod
    def encrypt_str(param):
        return 'MerchantID=%s&CustID=%s&CustIP=%s&Curr=%s&Amount=%s&RefID=%s&' \
               'TransTime=%s&ReturnURL=%s&RequestURL=%s&BankCode=%s' % (
                   param['MerchantID'], param['CustID'], param['CustIP'], param['Curr'], param['Amount'],
                   param['RefID'], param['TransTime'], param['ReturnURL'], param['RequestURL'], param['BankCode']
               )

    @staticmethod
    def format_str(key, param):
        return '%s:%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s' % (
            key, param['MerchantID'], param['CustID'], param['CustIP'], param['Curr'], param['Amount'], param['RefID'],
            param['TransTime'], param['ReturnURL'], param['RequestURL'], param['BankCode'], param.get('Remarks', ''))

    # 下单
    def create_charge(self, pay, pay_amount, info):
        app_id = info['app_id']
        api_key = self.get_api_key(APP_CONF, app_id)
        # 数据
        data = OrderedDict((
            ('MerchantID', app_id),  # 商户ID
            ('CustID', str(pay.id)),  # 用户的
            ('CustIP', ''),  # 客户端ip
            ('Curr', 'CNY'),  # 货币 人民币
            ('Amount', '%.2f' % (float(pay_amount))),  # 订单金额 元
            ('RefID', str(pay.id)),  # 商户订单号
            ('TransTime', ''),  # 下单时间
            ('ReturnURL', '{}/pay/api/{}/new/xpay/'.format(
                settings.NOTIFY_PREFIX, settings.RETURN_PATH)),
            ('RequestURL', '{}/pay/api/{}/new/xpay/{}'.format(
                settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id)),
            ('BankCode', ''),  # 银行编码
        ))
        # 待加密字符串格式
        encrypt_str = self.encrypt_str(data)
        # 数据加密
        encrypt_data = self.encrypt_data(encrypt_str)
        # 待签名字符串格式
        format_str = self.format_str(api_key, data)
        # 数据加签
        data_sign = self.md5_sign_upper(format_str)
        # 请求数据
        parameter_dict = OrderedDict((
            ('Data', encrypt_data),
            ('EncryptText', data_sign),
            ('Remarks', ''),
        ))

        json_parameter = json.dumps(data)
        _LOGGER.info("xpay create date: %s, order_id is: %s", json_parameter,
                     data['RefID'])
        html_text = self.html_build_form(parameter_dict, self.get_gateway(APP_CONF))
        cache_id = redis_cache.save_html(pay.id, html_text)
        url = settings.PAY_CACHE_URL + cache_id
        _LOGGER.info('xpay pay url: %s', url)
        return {'charge_info': url}

    # success异步回调
    def check_notify_sign(self, request, app_id):
        _LOGGER.info("xpay notify body: %s", request.body)
        response_data = dict(request.GET.iteritems())
        data = response_data['Data']
        # 解密数据
        data = self.str_to_dict(self.decrypt_data(data))
        _LOGGER.info("xpay notify data: %s, order_id is: %s", data, data['RefID'])
        self.verify_notify_sign(data, self.get_api_key(APP_CONF, app_id))
        pay_id = data['RefID']
        pay = self.check_ip_pay_status(request, pay_id, channel_name='xpay', data=data)
        if not pay:
            return '%s||%s' % (data['TransID'], data['ValidationKey'])

        mch_id = pay.mch_id
        trade_status = str(data['Status'])
        trade_no = data['RefID']
        total_fee = float(data['Amount'])
        extend = {
            'trade_status': trade_status,
            'trade_no': trade_no,
            'total_fee': total_fee,
        }
        if trade_status == '002':
            self.check_channel_order(pay_id, total_fee, app_id)
            _LOGGER.info('xpay check order success, user_id:%s pay_id:%s' % (mch_id, pay_id))
            add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
            # async notify
            async_job.notify_mch(pay_id)
            return '%s||%s' % (data['TransID'], data['ValidationKey'])

    # 订单查询
    def query_charge(self, pay_order, app_id):
        pay_id = pay_order.id
        api_key = self.get_api_key(APP_CONF, app_id)
        pay = get_pay(pay_id)
        parameter_dict = OrderedDict((
            ('partner_code', app_id),  # 商户订单号
            ('currency', 'CNY'),  # 货币
            ('orderid', str(pay.id)),  # 商户订单号
        ))
        # 待签名字符串
        pending_str = 'partner_code=%s&currency=%s&orderid=%s&key=%s' % (
            parameter_dict['partner_code'], parameter_dict['currency'], parameter_dict['orderid'], api_key
        )
        parameter_dict['sign'] = sha1(pending_str).hexdigest().upper()

        _LOGGER.info('xpay query data %s, order_id is: %s', json.dumps(parameter_dict),
                     parameter_dict['orderid'])
        headers = {'Content-Type': 'application/x-www-form-urlencoded'}
        response = requests.post(self.get_query_geteway(APP_CONF), data=parameter_dict, headers=headers, timeout=5)

        xml_data = xmltodict.parse(response.text)
        data = xml_data['xml']
        err = data.get('error')
        if not err:
            data = data['items']['item']
            trade_status = data['status']
            total_fee = float(data.get('amount'))
            trade_no = str(data['partner_orderid'])
            extend = {
                'trade_status': trade_status,
                'trade_no': trade_no,
                'total_fee': total_fee,
            }
            if trade_status == '002':
                self.check_channel_order(pay_id, total_fee, app_id)
                _LOGGER.info('xpay query order success, mch_id:%s pay_id:%s' % (pay_order.mch_id, pay_id))
                add_pay_success(pay_order.mch_id, pay_id, total_fee, trade_no, extend)
                async_job.notify_mch(pay_order.id)
            else:
                _LOGGER.info('xpay query order success,but not pay success,status:%s ' % data['status'])
        else:
            _LOGGER.warn('xpay query error, error msg: %s', data['error'])


def create_charge(pay, pay_amount, info):
    return XPay().create_charge(pay, pay_amount, info)


def check_notify_sign(request, app_id):
    return XPay().check_notify_sign(request, app_id)


def query_charge(pay_order, app_id):
    XPay().query_charge(pay_order, app_id)
